#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <vmath.h>
#include <xmltree.h>
#include <bvol.h>
#include <object.h>

struct sphere {
	float rad;

	struct bsphere bsph;
	struct baabox bbox;
};

void free_data(void *data)
{
	free(data);
}

void *read_xml(struct xml_node *xml)
{
	struct sphere *sph;
	struct xml_node *sph_node;
	struct xml_attr *attr;

	sph_node = xml->chld;
	while(sph_node && strcmp(sph_node->name, "sphere") != 0) {
		sph_node = sph_node->next;
	}
	if(!sph_node) {
		return 0;
	}

	if(!(sph = malloc(sizeof *sph))) {
		return 0;
	}

	if((attr = xml_get_attr(sph_node, "rad"))) {
		sph->rad = attr->fval;
	} else {
		sph->rad = 1.0;	/* default radius */
	}

	return sph;
}

struct xml_tree *write_xml(void *data)
{
	return 0;	/* TODO */
}

struct bsphere *get_bsph(void *data, mat4_t xform)
{
	struct sphere *s = data;

	s->bsph.center = v3_transform(v3_cons(0, 0, 0), xform);
	s->bsph.rad = s->rad;

	return &s->bsph;
}

struct baabox *get_bbox(void *data, mat4_t xform)
{
	struct sphere *s = data;

	s->bbox.min.x = s->bbox.min.y = -s->rad;
	s->bbox.max.x = s->bbox.max.y = s->rad;

	s->bbox.min = v3_transform(s->bbox.min, xform);
	s->bbox.max = v3_transform(s->bbox.max, xform);

	return &s->bbox;
}

void print_info(void *data, FILE *fp)
{
	struct sphere *s = data;
	fprintf(fp, "sphere: radius: %f\n", s->rad);
}

int intersect(void *data, mat4_t xform, mat4_t inv_xform, ray_t ray, struct isect_point *pt)
{
	double a, b, c, d, sqrt_d, t1, t2;
	struct sphere *s = data;

	/* bring the ray in object space */
	ray = ray_transform(ray, inv_xform);

	a = SQ(ray.dir.x) + SQ(ray.dir.y) + SQ(ray.dir.z);
	b = 2.0 * ray.dir.x * ray.origin.x + 2.0 * ray.dir.y * ray.origin.y +
		2.0 * ray.dir.z * ray.origin.z;
	c = SQ(ray.origin.x) + SQ(ray.origin.y) + SQ(ray.origin.z) - SQ(s->rad);

	if((d = SQ(b) - 4.0 * a * c) < 0.0) {
		return 0;
	}

	sqrt_d = sqrt(d);
	t1 = (-b + sqrt_d) / (2.0 * a);
	t2 = (-b - sqrt_d) / (2.0 * a);

	if((t1 < ERROR_MARGIN && t2 < ERROR_MARGIN) || (t1 > 1.0 && t2 > 1.0)) {
		return 0;
	}

	if(pt) {
		mat4_t invtrans;
		if(t1 < ERROR_MARGIN) t1 = t2;
		if(t2 < ERROR_MARGIN) t2 = t1;

		pt->dist = t1 < t2 ? t1 : t2;
		
		pt->pos.x = ray.origin.x + ray.dir.x * pt->dist;
		pt->pos.y = ray.origin.y + ray.dir.y * pt->dist;
		pt->pos.z = ray.origin.z + ray.dir.z * pt->dist;

		pt->normal.x = pt->pos.x / s->rad;
		pt->normal.y = pt->pos.y / s->rad;
		pt->normal.z = pt->pos.z / s->rad;

		pt->pos = v3_transform(pt->pos, xform);

		m4_transpose(invtrans, inv_xform);
		pt->normal = v3_transform(pt->normal, invtrans);

		/* TODO add texcoords */
	}
	return 1;
}
